home *** CD-ROM | disk | FTP | other *** search
/ BCI NET 2 / BCI NET 2.iso / archives / telecomm / misc / xprzmodem.lha / Source / utils.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-02-18  |  40.5 KB  |  1,466 lines

  1. /**********************************************************************
  2.  * Utils.c: Miscellaneous support routines for xprzmodem.library;
  3.  * Version 2.10, 12 February 1991, by Rick Huebner.
  4.  * Released to the Public Domain; do as you like with this code.
  5.  *
  6.  * Version 2.50, 15 November 1991, by William M. Perkins.  Added code
  7.  * to update_rate() function in utils.c to avoid the Guru # 80000005
  8.  * you would have gotten if you had decided to adjust the system clock
  9.  * back during an upload or download.
  10.  *
  11.  * Mysprintf() function to replace sprintf() and proto code to use
  12.  * libinit.o and linent.o library code was supplied by Jim Cooper of SAS.
  13.  *
  14.  * Version 2.54, 28 June 1993, by Olaf `Olsen' Barthel. Got rid of the
  15.  * timer.c code, added OS 2.0 dependencies, rewrote update_rate() to
  16.  * display only the information which was really available, added
  17.  * control-code escaping & run-length encoding, changed the abort
  18.  * routine to pay attention to the different abort levels, removed
  19.  * SAS/C library dependencies (octal conversion, etc.), added
  20.  * support for proper file creation date and access permission bits.
  21.  **********************************************************************/
  22.  
  23. /* Transfer options to use if XProtocolSetup not called */
  24. struct SetupVars Default_Config =
  25. {
  26.   NULL, NULL, 0,
  27.   {"C"},
  28.   {"N"},
  29.   {"16"},
  30.   {"0"},
  31.   {"10"},
  32.   {"N"},
  33.   {"N"},
  34.   {"N"},
  35.   {"Y"},
  36.   {"N"},
  37.   {"Y"},
  38.   {""},
  39.   {"1024"},
  40.   {"S"}
  41. };
  42.  
  43. /* Global data. */
  44. struct DosLibrary *DOSBase;
  45. struct ExecBase *SysBase;
  46. struct Library *UtilityBase, *LocaleBase, *TimerBase;
  47. struct Catalog *Catalog;
  48. struct timerequest TimeRequest;
  49.  
  50. /* Offset to convert local time into GMT. */
  51. LONG GMT_Offset = UTC_OFFSET;
  52.  
  53. /* So we know who we are. */
  54. STATIC LONG LibVersion, LibRevision;
  55.  
  56. /* This gets called by the SAS/C library init code. */
  57. int __saveds __asm __UserLibInit(register __a6 struct MyLibrary *libbase)
  58. {
  59.   /* Save these for later. */
  60.   LibVersion = ((struct Library *) libbase)->lib_Version;
  61.   LibRevision = ((struct Library *) libbase)->lib_Revision;
  62.  
  63.   /* All the code depends on this. */
  64.   SysBase = *(struct ExecBase **) 4;
  65.  
  66.   /* Already set up? */
  67.   if (DOSBase)
  68.     return (NULL);
  69.   else
  70.   {
  71.     /* Now open all the libraries we need. */
  72.     if (DOSBase = (struct DosLibrary *) OpenLibrary("dos.library", 37))
  73.     {
  74.       if (UtilityBase = OpenLibrary("utility.library", 37))
  75.       {
  76.     if (!OpenDevice(TIMERNAME, UNIT_VBLANK, &TimeRequest, NULL))
  77.     {
  78.       TimerBase = (struct Library *) TimeRequest.tr_node.io_Device;
  79.  
  80.           /* Try to open locale.library. */
  81.       if (LocaleBase = OpenLibrary("locale.library", 38))
  82.       {
  83.         struct Locale *Locale;
  84.  
  85.             /* Try to open the catalog. */
  86.         if (Catalog = OpenCatalogA(NULL, "xprzmodem.catalog", NULL))
  87.         {
  88.           /* Exclude all other catalogs, this is to avoid
  89.            * conflicts with xprzmodem 3.1 catalog files.
  90.            */
  91.           if (Catalog->cat_Version < 17)
  92.           {
  93.         CloseCatalog(Catalog);
  94.  
  95.         Catalog = NULL;
  96.           }
  97.         }
  98.  
  99.             /* Open the current default locale. */
  100.         Locale = OpenLocale(NULL);
  101.  
  102.             /* Calculate time offset for GMT. */
  103.         GMT_Offset = 60 * Locale->loc_GMTOffset + UTC_OFFSET;
  104.  
  105.         CloseLocale(Locale);
  106.       }
  107.  
  108.           /* Return success. */
  109.       return (NULL);
  110.     }
  111.  
  112.     CloseLibrary(UtilityBase);
  113.  
  114.     UtilityBase = NULL;
  115.       }
  116.  
  117.       CloseLibrary(DOSBase);
  118.  
  119.       DOSBase = NULL;
  120.     }
  121.   }
  122.  
  123.   return (~0);
  124. }
  125.  
  126. /* This gets called by the SAS/C library cleanup code. */
  127. void __saveds __asm __UserLibCleanup(register __a6 struct MyLibrary *libbase)
  128. {
  129.   if (TimerBase)
  130.   {
  131.     CloseDevice(&TimeRequest);
  132.  
  133.     TimerBase = NULL;
  134.   }
  135.  
  136.   if (DOSBase)
  137.   {
  138.     CloseLibrary(DOSBase);
  139.  
  140.     DOSBase = NULL;
  141.   }
  142.  
  143.   if (UtilityBase)
  144.   {
  145.     CloseLibrary(UtilityBase);
  146.  
  147.     UtilityBase = NULL;
  148.   }
  149.  
  150.   if (Catalog)
  151.   {
  152.     CloseCatalog(Catalog);
  153.  
  154.     Catalog = NULL;
  155.   }
  156.  
  157.   if (LocaleBase)
  158.   {
  159.     CloseLibrary(LocaleBase);
  160.  
  161.     LocaleBase = NULL;
  162.   }
  163. }
  164.  
  165. /* Convert a string to all upper case characters. */
  166. STATIC VOID __regargs
  167. string_upper(register STRPTR buf)
  168. {
  169.   register UBYTE c;
  170.  
  171.   while (c = *buf)
  172.     *buf++ = ToUpper(c);
  173. }
  174.  
  175. /* Skip blanks in a string. */
  176. STATIC STRPTR __regargs
  177. skip_blank(register STRPTR buf)
  178. {
  179.   register UBYTE c;
  180.  
  181.   while (c = *buf)
  182.   {
  183.     if (c == ' ' || c == ',' || c == '\t' || c == '\r' || c == '\n')
  184.       buf++;
  185.     else
  186.       break;
  187.   }
  188.  
  189.   return (buf);
  190. }
  191.  
  192. /* Skip non-blank chars in a string. */
  193. STATIC STRPTR __regargs
  194. skip_chars(register STRPTR buf)
  195. {
  196.   register UBYTE c;
  197.  
  198.   while (c = *buf)
  199.   {
  200.     if (c == ' ' || c == ',' || c == '\t' || c == '\r' || c == '\n')
  201.       return (buf);
  202.     else
  203.       buf++;
  204.   }
  205.  
  206.   return (buf);
  207. }
  208.  
  209. /* Look up a string in the locale database. */
  210. STRPTR GetLocaleString(LONG ID)
  211. {
  212.   extern struct
  213.   {
  214.     LONG as_ID;
  215.     STRPTR as_Str;
  216.   } *AppStrings;
  217.  
  218.   extern LONG NumAppStrings;
  219.  
  220.   STRPTR Builtin;
  221.  
  222.   if (ID < NumAppStrings && AppStrings[ID].as_ID == ID)
  223.     Builtin = AppStrings[ID].as_Str;
  224.   else
  225.   {
  226.     WORD i;
  227.  
  228.     Builtin = "";
  229.  
  230.     for (i = 0; i < NumAppStrings; i++)
  231.     {
  232.       if (AppStrings[i].as_ID == ID)
  233.       {
  234.     Builtin = AppStrings[i].as_Str;
  235.  
  236.     break;
  237.       }
  238.     }
  239.   }
  240.  
  241.   if (LocaleBase && Catalog)
  242.   {
  243.     STRPTR String = GetCatalogStr(Catalog, ID, Builtin);
  244.  
  245.     if (String[0])
  246.       return (String);
  247.     else
  248.       return (Builtin);
  249.   }
  250.   else
  251.     return (Builtin);
  252. }
  253.  
  254. /* Get the local time, convert it to GMT. */
  255. ULONG getsystime(struct timeval * tv)
  256. {
  257.   struct timeval Now;
  258.  
  259.   GetSysTime(&Now);
  260.  
  261.   Now.tv_secs += GMT_Offset;
  262.  
  263.   if (tv)
  264.   {
  265.     tv->tv_secs = Now.tv_secs;
  266.     tv->tv_micro = 0;
  267.   }
  268.  
  269.   return (Now.tv_secs);
  270. }
  271.  
  272. /* Our reentrant sprintf() clone. */
  273. VOID __stdargs mysprintf(STRPTR Buffer, STRPTR Format,...)
  274. {
  275.   va_list VarArgs;
  276.  
  277.   va_start(VarArgs, Format);
  278.   RawDoFmt(Format, VarArgs, (VOID(*)())(VOID(*))"\x16\xC0\x4E\x75", Buffer);
  279.   va_end(VarArgs);
  280. }
  281.  
  282. /* Convert a string into numeric data. */
  283. LONG Atol(STRPTR Buffer)
  284. {
  285.   LONG Value;
  286.  
  287.   StrToLong(Buffer, &Value);
  288.  
  289.   return (Value);
  290. }
  291.  
  292. /**********************************************************
  293.  *      long XProtocolSetup(struct XPR_IO *xio)
  294.  *
  295.  * Called by comm program to set transfer options
  296.  **********************************************************/
  297. long __saveds __asm XProtocolSetup(register __a0 struct XPR_IO *xio)
  298. {
  299.   struct SetupVars *sv, tempsv;
  300.   struct xpr_option *option_ptrs[15];
  301.   struct xpr_option *optr, xo_hdr, xo_t, xo_o, xo_b, xo_f, xo_e, xo_s,
  302.     xo_c, xo_r, xo_a, xo_d, xo_k, xo_p, xo_m, xo_i;
  303.   UBYTE buf[512], *p;
  304.   long i, len;
  305.  
  306.   /* Allocate memory for transfer options string */
  307.   if (!(sv = (void *) xio->xpr_data))
  308.   {
  309.     xio->xpr_data = AllocMem((long) sizeof(struct SetupVars), MEMF_PUBLIC | MEMF_CLEAR);    /* OLSEN */
  310.  
  311.     if (!(sv = (void *) xio->xpr_data))
  312.     {
  313.       ioerr(xio, GetLocaleString(MSG_NOT_ENOUGH_MEMORY_TXT));
  314.       return XPRS_FAILURE;
  315.     }
  316.     /* Start out with default options; merge user changes into defaults */
  317.     *sv = Default_Config;
  318.   }
  319.  
  320.   /* If options string passed by comm prog, use it; else prompt user */
  321.   if (xio->xpr_filename)
  322.     strcpy(buf, xio->xpr_filename);
  323.   else
  324.   {
  325.     /* If xpr_options() implemented by comm program, use it */
  326.     if (xio->xpr_extension >= 1 && xio->xpr_options)
  327.     {
  328.       /* Set up protocol string. */
  329.       mysprintf(buf, GetLocaleString(MSG_ZMODEM_OPTIONS_TXT), LibVersion, LibRevision);
  330.  
  331.       /* Let user edit temp copy of options so we can ignore invalid
  332.        * entries.  Have to init all this crud the hard way 'cause it's
  333.        * got to be on the stack in order to maintain reentrancy
  334.        */
  335.       tempsv = *sv;
  336.       xo_hdr.xpro_description = buf;
  337.       xo_hdr.xpro_type = XPRO_HEADER;
  338.       xo_hdr.xpro_value = NULL;
  339.       xo_hdr.xpro_length = 0;
  340.       option_ptrs[0] = &xo_hdr;
  341.  
  342.       xo_t.xpro_description = GetLocaleString(MSG_ZMODEM_TEXT_MODE_TXT);
  343.       xo_t.xpro_type = XPRO_STRING;
  344.       xo_t.xpro_value = tempsv.option_t;
  345.       xo_t.xpro_length = sizeof(tempsv.option_t);
  346.       option_ptrs[1] = &xo_t;
  347.  
  348.       xo_o.xpro_description = GetLocaleString(MSG_ZMODEM_OVERWRITE_MODE_TXT);
  349.       xo_o.xpro_type = XPRO_STRING;
  350.       xo_o.xpro_value = tempsv.option_o;
  351.       xo_o.xpro_length = sizeof(tempsv.option_o);
  352.       option_ptrs[2] = &xo_o;
  353.  
  354.       xo_b.xpro_description = GetLocaleString(MSG_ZMODEM_IO_BUFFER_SIZE_TXT);
  355.       xo_b.xpro_type = XPRO_LONG;
  356.       xo_b.xpro_value = tempsv.option_b;
  357.       xo_b.xpro_length = sizeof(tempsv.option_b);
  358.       option_ptrs[3] = &xo_b;
  359.  
  360.       xo_f.xpro_description = GetLocaleString(MSG_ZMODEM_FRAME_SIZE_TXT);
  361.       xo_f.xpro_type = XPRO_LONG;
  362.       xo_f.xpro_value = tempsv.option_f;
  363.       xo_f.xpro_length = sizeof(tempsv.option_f);
  364.       option_ptrs[4] = &xo_f;
  365. /*
  366.       xo_m.xpro_description = GetLocaleString(MSG_ZMODEM_MAXIMUM_PACKET_SIZE_TXT);
  367.       xo_m.xpro_type = XPRO_LONG;
  368.       xo_m.xpro_value = tempsv.option_m;
  369.       xo_m.xpro_length = sizeof(tempsv.option_m);
  370.       option_ptrs[5] = &xo_m;
  371.  */
  372.       xo_e.xpro_description = GetLocaleString(MSG_ZMODEM_ERROR_LIMIT_TXT);
  373.       xo_e.xpro_type = XPRO_LONG;
  374.       xo_e.xpro_value = tempsv.option_e;
  375.       xo_e.xpro_length = sizeof(tempsv.option_e);
  376.       option_ptrs[5] = &xo_e;
  377.  
  378.       xo_i.xpro_description = GetLocaleString(MSG_FILE_ATTRIBUTES_TXT);
  379.       xo_i.xpro_type = XPRO_STRING;
  380.       xo_i.xpro_value = tempsv.option_i;
  381.       xo_i.xpro_length = sizeof(tempsv.option_i);
  382.       option_ptrs[6] = &xo_i;
  383.  
  384.       xo_c.xpro_description = GetLocaleString(MSG_ZMODEM_ESCAPE_CONTROL_CHARACTERS_TXT);    /* OLSEN */
  385.       xo_c.xpro_type = XPRO_BOOLEAN;
  386.       xo_c.xpro_value = tempsv.option_c;
  387.       xo_c.xpro_length = sizeof(tempsv.option_c);
  388.       option_ptrs[7] = &xo_c;
  389.  
  390.       xo_a.xpro_description = GetLocaleString(MSG_ZMODEM_AUTO_ACTIVATE_RECEIVER_TXT);
  391.       xo_a.xpro_type = XPRO_BOOLEAN;
  392.       xo_a.xpro_value = tempsv.option_a;
  393.       xo_a.xpro_length = sizeof(tempsv.option_a);
  394.       option_ptrs[8] = &xo_a;
  395.  
  396.       xo_d.xpro_description = GetLocaleString(MSG_ZMODEM_DELETE_AFTER_SENDING_TXT);
  397.       xo_d.xpro_type = XPRO_BOOLEAN;
  398.       xo_d.xpro_value = tempsv.option_d;
  399.       xo_d.xpro_length = sizeof(tempsv.option_d);
  400.       option_ptrs[9] = &xo_d;
  401.  
  402.       xo_k.xpro_description = GetLocaleString(MSG_ZMODEM_KEEP_PARTIAL_FILES_TXT);
  403.       xo_k.xpro_type = XPRO_BOOLEAN;
  404.       xo_k.xpro_value = tempsv.option_k;
  405.       xo_k.xpro_length = sizeof(tempsv.option_k);
  406.       option_ptrs[10] = &xo_k;
  407.  
  408.       xo_s.xpro_description = GetLocaleString(MSG_ZMODEM_SEND_FULL_PATH_TXT);
  409.       xo_s.xpro_type = XPRO_BOOLEAN;
  410.       xo_s.xpro_value = tempsv.option_s;
  411.       xo_s.xpro_length = sizeof(tempsv.option_s);
  412.       option_ptrs[11] = &xo_s;
  413.  
  414.       xo_r.xpro_description = GetLocaleString(MSG_ZMODEM_USE_RECEIVED_PATH_TXT);
  415.       xo_r.xpro_type = XPRO_BOOLEAN;
  416.       xo_r.xpro_value = tempsv.option_r;
  417.       xo_r.xpro_length = sizeof(tempsv.option_r);
  418.       option_ptrs[12] = &xo_r;
  419.  
  420.       xo_p.xpro_description = GetLocaleString(MSG_ZMODEM_DEFAULT_RECEIVE_PATH_TXT);
  421.       xo_p.xpro_type = XPRO_STRING;
  422.       xo_p.xpro_value = tempsv.option_p;
  423.       xo_p.xpro_length = sizeof(tempsv.option_p);
  424.       option_ptrs[13] = &xo_p;
  425.  
  426.       /* Convert Y/N used elsewhere into "yes"/"no" required by spec */
  427.       for (i = 7; i <= 12; ++i)    /* OLSEN */
  428.       {
  429.     optr = option_ptrs[i];
  430.     strcpy(optr->xpro_value, (*optr->xpro_value == 'Y') ? "yes" : "no");
  431.       }
  432.  
  433.       (*xio->xpr_options) (14L, option_ptrs);    /* OLSEN */
  434.  
  435.       /* Convert "yes"/"no" or "on"/"off" into Y/N */
  436.       for (i = 7; i <= 12; ++i)    /* OLSEN */
  437.       {
  438.     optr = option_ptrs[i];
  439.  
  440.     if (!Stricmp(optr->xpro_value, "yes") || !Stricmp(optr->xpro_value, "on"))
  441.       strcpy(optr->xpro_value, "Y");
  442.     else
  443.       strcpy(optr->xpro_value, "N");
  444.       }
  445.  
  446.       /* Convert xpr_options() results into parseable options string */
  447.       mysprintf(buf, "T%s,O%s,B%s,F%s,E%s,C%s,A%s,D%s,K%s,S%s,R%s,I%s,P%s",
  448.     tempsv.option_t, tempsv.option_o, tempsv.option_b, tempsv.option_f,
  449.     tempsv.option_e, tempsv.option_c, tempsv.option_a, tempsv.option_d, tempsv.option_k,
  450.     tempsv.option_s, tempsv.option_r, tempsv.option_i, tempsv.option_p);
  451.       /* If xpr_options() not provided, try xpr_gets() instead */
  452.     }
  453.     else
  454.     {
  455.       /* Start buffer with current settings so user can see/edit them. */
  456.       mysprintf(buf, "T%s,O%s,B%s,F%s,E%s,C%s,A%s,D%s,K%s,S%s,R%s,I%s,P%s",
  457.     sv->option_t, sv->option_o, sv->option_b, sv->option_f,
  458.     sv->option_e, sv->option_c, sv->option_a, sv->option_d, sv->option_k,
  459.     sv->option_s, sv->option_r, sv->option_i, sv->option_p);
  460.       if (xio->xpr_gets)
  461.     (*xio->xpr_gets) (GetLocaleString(MSG_ZMODEM_OPTIONS2_TXT), buf);
  462.     }
  463.   }
  464.   /* Upshift options string for easier parsing */
  465.  
  466.   string_upper(buf);
  467.  
  468.   /* Merge new T(ext) option into current settings if given
  469.    *  "TY" = Force Text mode on,
  470.    *  "TN" = Force Text mode off,
  471.    *  "T?" = Use other end's text mode suggestion (default to binary)
  472.    *  "TC" = Ask Comm program for file type
  473.    */
  474.   if (p = find_option(buf, 'T'))
  475.   {
  476.     if (*p == 'Y' || *p == 'N' || *p == '?' || *p == 'C')
  477.       *sv->option_t = *p;
  478.     else
  479.       ioerr(xio, GetLocaleString(MSG_INVALID_FLAG_T_IGNORED_TXT));
  480.   }
  481.  
  482.   /* Merge new O(verwrite) option into current settings if given
  483.    *  "OY" = Yes, delete old file and replace with new one,
  484.    *  "ON" = No, prevent overwrite by appending ".dup" to avoid name collision,
  485.    *  "OR" = Resume transfer at end of existing file,
  486.    *  "OS" = Skip file if it already exists; go on to next
  487.    */
  488.   if (p = find_option(buf, 'O'))
  489.   {
  490.     if (*p == 'R' && !xio->xpr_finfo)
  491.       ioerr(xio, GetLocaleString(MSG_CANT_RESUME_TXT));
  492.     else if (*p == 'Y' || *p == 'N' || *p == 'R' || *p == 'S')
  493.       *sv->option_o = *p;
  494.     else
  495.       ioerr(xio, GetLocaleString(MSG_INVALID_FLAG_O_IGNORED_TXT));
  496.   }
  497.  
  498.   /* Merge new B(uffer) setting into current settings if given
  499.    * Size of file I/O buffer in kilobytes
  500.    */
  501.   if (p = find_option(buf, 'B'))
  502.   {
  503.     len = Atol(p);
  504.     if (len < 1)
  505.       len = 1;
  506.     mysprintf(sv->option_b, "%ld", len);
  507.   }
  508.  
  509.   /* Merge new F(ramelength) setting into other settings if given
  510.    * Number of bytes we're willing to send or receive between ACKs.
  511.    * 0 = unlimited; nonstop streaming data
  512.    */
  513.   if (p = find_option(buf, 'F'))
  514.   {
  515.     len = Atol(p);
  516.     if (len < 0)
  517.       len = 0;
  518.     if (len > 0 && len < MINBLOCK)
  519.       len = MINBLOCK;
  520.     mysprintf(sv->option_f, "%ld", len);
  521.   }
  522.  
  523.   /* Merge new E(rror limit) setting into other settings if given
  524.    * Number of sequential errors which will cause an abort
  525.    */
  526.   if (p = find_option(buf, 'E'))
  527.   {
  528.     len = Atol(p);
  529.     if (len < 1)
  530.       len = 1;
  531.     if (len > 32767)
  532.       len = 32767;
  533.     mysprintf(sv->option_e, "%ld", len);
  534.   }
  535.  
  536.   /* Merge new A(uto-activate) setting into other settings if given
  537.    *  "AY" = Automatically call XProtocolReceive() if ZRQINIT string received
  538.    *  "AN" = Don't look for ZRQINIT; user will explicitly activate receive
  539.    */
  540.   if (p = find_option(buf, 'A'))
  541.   {
  542.     if (*p == 'Y' || *p == 'N')
  543.       *sv->option_a = *p;
  544.     else
  545.       ioerr(xio, GetLocaleString(MSG_INVALID_FLAG_A_IGNORED_TXT));
  546.   }
  547.  
  548.   /* Merge new Escape C(ontrol) character setting into other settings if given
  549.    *  "CY" = Escape control characters
  550.    *  "CN" = Don't do it
  551.    */
  552.   if (p = find_option(buf, 'C'))
  553.   {
  554.     if (*p == 'Y' || *p == 'N')
  555.       *sv->option_c = *p;
  556.     else
  557.       ioerr(xio, GetLocaleString(MSG_INVALID_FLAG_C_IGNORED_TXT));
  558.   }
  559.  
  560.   /* Merge new D(elete after sending) setting into other options
  561.    *  "DY" = Delete files after successfully sending them
  562.    *  "DN" = Don't delete files after sending
  563.    */
  564.   if (p = find_option(buf, 'D'))
  565.   {
  566.     if (*p == 'Y' && (xio->xpr_extension < 2 || !xio->xpr_unlink))
  567.       ioerr(xio, GetLocaleString(MSG_CANT_UNLINK_TXT));
  568.     else if (*p == 'Y' || *p == 'N')
  569.       *sv->option_d = *p;
  570.     else
  571.       ioerr(xio, GetLocaleString(MSG_INVALID_FLAG_D_IGNORED_TXT));
  572.   }
  573.  
  574.   /* Merge new K(eep partial files) setting into other options
  575.    *  "KY" = Keep partially-received file fragments to allow later resumption
  576.    *  "KN" = Delete partially-received file fragments
  577.    */
  578.   if (p = find_option(buf, 'K'))
  579.   {
  580.     if (*p == 'N' && (xio->xpr_extension < 2 || !xio->xpr_unlink))
  581.       ioerr(xio, GetLocaleString(MSG_CANT_KEEP_TXT));
  582.     else if (*p == 'Y' || *p == 'N')
  583.       *sv->option_k = *p;
  584.     else
  585.       ioerr(xio, GetLocaleString(MSG_INVALID_FLAG_K_IGNORED_TXT));
  586.   }
  587.  
  588.   /* Merge new S(end full path) setting into other options
  589.    *  "SY" = Send full filename including directory path to receiver
  590.    *  "SN" = Send only simple filename portion, not including directory path
  591.    */
  592.   if (p = find_option(buf, 'S'))
  593.   {
  594.     if (*p == 'Y' || *p == 'N')
  595.       *sv->option_s = *p;
  596.     else
  597.       ioerr(xio, GetLocaleString(MSG_INVALID_FLAG_S_IGNORED_TXT));
  598.   }
  599.  
  600.   /* Merge new R(eceive path) setting into other options
  601.    *  "RY" = Use full filename exactly as received; don't use P option path
  602.    *  "RN" = Ignore received directory path if any; use path from P option
  603.    */
  604.   if (p = find_option(buf, 'R'))
  605.   {
  606.     if (*p == 'Y' || *p == 'N')
  607.       *sv->option_r = *p;
  608.     else
  609.       ioerr(xio, GetLocaleString(MSG_INVALID_FLAG_R_IGNORED_TXT));
  610.   }
  611.  
  612.   /* Merge new file attribute setting into other options
  613.    *  "IS" = Transmit file attributes when sending
  614.    *  "IR" = Honor received file attributes
  615.    *  "IB" = Transmit file attributes when sending and honor then when
  616.    *         receiving
  617.    */
  618.   if (p = find_option(buf, 'I'))
  619.   {
  620.     if (*p == 'S' || *p == 'R' || *p == 'B')
  621.       *sv->option_i = *p;
  622.     else
  623.       ioerr(xio, GetLocaleString(MSG_INVALID_FLAG_I_IGNORED_TXT));
  624.   }
  625.  
  626.   /* Merge new P(ath) setting into other options
  627.    *  "Pdir" = Receive files into directory "dir" if RN selected
  628.    *           "dir" can by any valid existing directory, with or without trailing "/"
  629.    */
  630.   if (p = find_option(buf, 'P'))
  631.   {
  632.     strcpy(sv->option_p, p);
  633.     p = skip_chars(sv->option_p);
  634.     *p = '\0';
  635.   }
  636.  
  637.   /*
  638.    * Merge new M(aximum) packet setting into other settings.
  639.    */
  640.   if (p = find_option(buf, 'M'))
  641.   {
  642.     mysprintf(sv->option_m, "%ld", 1024);
  643.   }
  644.  
  645.   return (*sv->option_a == 'Y') ? XPRS_SUCCESS | XPRS_NORECREQ | XPRS_HOSTMON
  646.     : XPRS_SUCCESS | XPRS_NORECREQ;
  647. }                /* End of long XProtocolSetup() */
  648.  
  649. /**********************************************************
  650.  *      long XProtocolCleanup(struct XPR_IO *xio)
  651.  *
  652.  * Called by comm program to give us a chance to clean
  653.  * up before program ends
  654.  **********************************************************/
  655. long __saveds __asm XProtocolCleanup(register __a0 struct XPR_IO *xio)
  656. {
  657.   /* Release option memory, if any */
  658.   if (xio->xpr_data)
  659.   {
  660.     FreeMem(xio->xpr_data, (long) sizeof(struct SetupVars));
  661.  
  662.     xio->xpr_data = NULL;
  663.   }
  664.  
  665.   return XPRS_SUCCESS;
  666. }                /* End of long XProtocolCleanup() */
  667.  
  668. /**********************************************************
  669.  *      long XProtocolHostMon(struct XPR_IO *xio,
  670.  *         char *serbuff, long actual, long maxsize)
  671.  *
  672.  * Called by comm program upon our request (XPRS_HOSTMON)
  673.  * to let us monitor the incoming data stream for our
  674.  * receiver auto-activation string (ZRQINIT packet).
  675.  * We only ask for this to be called if option AY is set.
  676.  **********************************************************/
  677. long __saveds __asm XProtocolHostMon(register __a0 struct XPR_IO *xio,register __a1 char *serbuff,register __d0 long actual,register __d1 long maxsize)
  678. {
  679.   static UBYTE startrcv[] =
  680.   {ZPAD, ZDLE, ZHEX, '0', '0', 0};
  681.   struct SetupVars *sv;
  682.  
  683.   if (!(sv = (void *) xio->xpr_data))
  684.     return actual;        /* XProtocolSetup() never called?! */
  685.  
  686.   if (!sv->matchptr)
  687.     sv->matchptr = startrcv;
  688.  
  689.   /* Scan through serbuff to see if we can match all bytes in the start
  690.    * string in sequence.
  691.    */
  692.   for (sv->bufpos = serbuff; (ULONG) sv->bufpos < (ULONG) serbuff + actual; ++sv->bufpos)
  693.   {
  694.     if (*sv->bufpos == *sv->matchptr)
  695.     {                /* if data matches current position in match */
  696.       ++sv->matchptr;        /* string, increment match position */
  697.       if (!*sv->matchptr)
  698.       {                /* if at end of match string, it all matched */
  699.     sv->buflen = actual - (sv->bufpos - serbuff);
  700.     XProtocolReceive(xio);
  701.     sv->matchptr = startrcv;
  702.     actual = 0;
  703.     break;
  704.       }
  705.     }
  706.     else if (sv->matchptr > startrcv)
  707.     {                /* mismatch?  Reset to start of match string */
  708.       sv->matchptr = startrcv;
  709.       if (*sv->bufpos == *sv->matchptr)
  710.     ++sv->matchptr;
  711.     }
  712.   }
  713.  
  714.   sv->bufpos = NULL;
  715.   return actual;
  716. }
  717.  
  718. /**********************************************************
  719.  *      long XProtocolUserMon(struct XPR_IO *xio,
  720.  *         char *serbuff, long actual, long maxsize)
  721.  *
  722.  * Called by comm program to let us monitor user's inputs;
  723.  * we never ask for this to be called, but it's better to
  724.  * recover gracefully than guru the machine.
  725.  **********************************************************/
  726. long __saveds __asm XProtocolUserMon(register __a0 struct XPR_IO *xio,register __a1 char *serbuff,register __d0 long actual,register __d1 long maxsize)
  727. {
  728.   return actual;
  729. }
  730.  
  731. /**********************************************************
  732.  *      struct Vars *setup(struct XPR_IO *io)
  733.  *
  734.  * Perform setup and initializations common to both Send
  735.  * and Receive routines
  736.  **********************************************************/
  737. struct Vars *setup(struct XPR_IO *io)
  738. {
  739.   static long bauds[] =
  740.   {110, 300, 1200, 2400, 4800, 9600, 19200, 31250,
  741.    38400, 57600, 76800, 115200};
  742.   struct SetupVars *sv;
  743.   struct Vars *v;
  744.   long origbuf;
  745.  
  746.   /* Make sure comm program supports the required call-back functions */
  747.   if (!io->xpr_update)
  748.     return NULL;
  749.   if (!io->xpr_fopen || !io->xpr_fclose || !io->xpr_fread
  750.       || !io->xpr_fwrite || !io->xpr_fseek || !io->xpr_sread
  751.       || !io->xpr_swrite)
  752.   {
  753.     ioerr(io, GetLocaleString(MSG_COMM_PROGRAM_MISSING_FUNCTION_TXT));
  754.     return NULL;
  755.   }
  756.  
  757.   /* Hook in default transfer options if XProtocolSetup wasn't called */
  758.   if (!(sv = (void *) io->xpr_data))
  759.   {
  760.     io->xpr_data = AllocMem((long) sizeof(struct SetupVars), MEMF_PUBLIC | MEMF_CLEAR);        /* OLSEN */
  761.  
  762.     if (!(sv = (void *) io->xpr_data))
  763.     {
  764.       ioerr(io, GetLocaleString(MSG_NOT_ENOUGH_MEMORY_TXT));
  765.       return NULL;
  766.     }
  767.     *sv = Default_Config;
  768.   }
  769.  
  770.   /* Allocate memory for our unshared variables, to provide reentrancy */
  771.   if (!(v = AllocMem((long) sizeof(struct Vars), MEMF_PUBLIC | MEMF_CLEAR)))    /* OLSEN */
  772.   {
  773.   nomem:
  774.     ioerr(io, GetLocaleString(MSG_NOT_ENOUGH_MEMORY_TXT));
  775.     return NULL;
  776.   }
  777.   v->Modemchar = v->Modembuf;
  778.  
  779.   /*
  780.      * Allocate memory for our file I/O buffer; if we can't get as much as
  781.      * requested, keep asking for less until we hit minimum before giving up
  782.    */
  783.   v->Filebufmax = origbuf = Atol(sv->option_b) * 1024;
  784.   while (!(v->Filebuf = AllocMem(v->Filebufmax, 0L)))
  785.   {
  786.     if (v->Filebufmax > 1024)
  787.       v->Filebufmax -= 1024;
  788.     else
  789.     {
  790.       FreeMem(v, (long) sizeof(struct Vars));
  791.  
  792.       goto nomem;
  793.     }
  794.   }
  795.  
  796.   /* If framelength was intended to match buffer size, stay in sync */
  797.   v->Tframlen = Atol(sv->option_f);
  798.   if (v->Tframlen && v->Tframlen == origbuf)
  799.     v->Tframlen = v->Filebufmax;
  800.  
  801.   v->ErrorLimit = Atol(sv->option_e);
  802.  
  803.   /* If selected, enable control character escape mode. */
  804.   if (sv->option_c[0] == 'Y')
  805.     v->Zctlesc = 1;
  806.   else
  807.     v->Zctlesc = 0;
  808.  
  809.   /* Take care of the file attribute options. */
  810.   if (sv->option_i[0] == 'S')
  811.     v->FileAttributes = 1;
  812.   else
  813.   {
  814.     if (sv->option_i[0] == 'R')
  815.       v->FileAttributes = 2;
  816.     else
  817.       v->FileAttributes = 3;
  818.   }
  819.  
  820.   /* Get the maximum allowed packet size. */
  821.   if ((v->ksize = Atol(sv->option_m)) < MINBLOCK)
  822.     v->ksize = MINBLOCK;
  823.   else
  824.   {
  825.     if (v->ksize > KSIZE)
  826.       v->ksize = KSIZE;
  827.   }
  828.  
  829.   /* Copy caller's io struct into our Vars for easier passing */
  830.   v->io = *io;
  831.  
  832.   /* Get baud rate; set serial port mode if necessary (and possible) */
  833.   if (v->io.xpr_setserial)
  834.   {
  835.     v->Oldstatus = (*v->io.xpr_setserial) (-1L);
  836.     if (v->Oldstatus != -1)
  837.     {
  838. #if 0                /* OLSEN */
  839.       ULONG newstatus;
  840.  
  841.       /* ZModem requires 8 data bits, no parity (full transparency),
  842.        *  leave other settings alone
  843.        */
  844.       newstatus = v->Oldstatus & 0xFFFFE0BC;
  845.       /* newstatus |= on_flags; Here's where we'd turn bits on if we
  846.        * needed to
  847.        */
  848.       if (newstatus != v->Oldstatus)
  849.     (*v->io.xpr_setserial) (newstatus);
  850.       v->Baud = bauds[(newstatus >> 16) & 0xFF];
  851. #else
  852.       v->Baud = bauds[(v->Oldstatus >> 16) & 0xFF];
  853.       v->Oldstatus = -1;
  854. #endif
  855.     }
  856.     else
  857.       v->Baud = 2400;
  858.     /* If no xpr_setserial(), muddle along with most likely guess */
  859.   }
  860.   else
  861.     v->Baud = 2400;
  862.  
  863.   return v;
  864. }                /* End of struct Vars *setup() */
  865.  
  866. /**********************************************************
  867.  *      void set_textmode(struct Vars *v)
  868.  *
  869.  * Set text/binary mode flags in accordance with T option
  870.  * setting
  871.  **********************************************************/
  872. void set_textmode(struct Vars *v)
  873. {
  874.   struct SetupVars *sv;
  875.   long i;
  876.  
  877.   sv = (void *) v->io.xpr_data;
  878.   switch (*sv->option_t)
  879.   {
  880.   case 'Y':            /* Force text mode on receive; suggest text mode on send */
  881.   TY:
  882.     v->Rxascii = TRUE;
  883.     v->Rxbinary = FALSE;
  884.     v->Lzconv = ZCNL;
  885.     break;
  886.   case 'N':            /* Force binary mode on receive; suggest binary mode on send */
  887.   TN:
  888.     v->Rxascii = FALSE;
  889.     v->Rxbinary = TRUE;
  890.     v->Lzconv = ZCBIN;
  891.     break;
  892.   case 'C':            /* Ask comm program for proper mode for this file */
  893.     if (v->io.xpr_finfo)
  894.     {
  895.       i = (*v->io.xpr_finfo) (v->Filename, 2L);
  896.       if (i == 1)        /* Comm program says use binary mode */
  897.     goto TN;
  898.       if (i == 2)        /* Comm program says use text mode */
  899.     goto TY;
  900.     }
  901.     /* xpr_finfo() not provided (or failed); default to T? */
  902.   case '?':
  903.     v->Rxascii = v->Rxbinary = FALSE;
  904.     v->Lzconv = 0;
  905.     break;
  906.   }
  907. }                /* End of void set_textmode() */
  908.  
  909. /**********************************************************
  910.  *      UBYTE *find_option(UBYTE *buf, UBYTE option)
  911.  *
  912.  * Search for specified option setting in string
  913.  **********************************************************/
  914. UBYTE *find_option(UBYTE * buf, UBYTE option)
  915. {
  916.   while (*buf)
  917.   {
  918.     buf = skip_blank(buf);
  919.  
  920.     if (*buf == option)
  921.       return ++buf;
  922.  
  923.     buf = skip_chars(buf);
  924.   }
  925.  
  926.   return NULL;
  927. }                /* End of UBYTE *find_option() */
  928.  
  929. /**********************************************************
  930.  *      void canit(struct Vars *v)
  931.  *
  932.  * send cancel string to get the other end to shut up
  933.  **********************************************************/
  934. void canit(struct Vars *v)
  935. {
  936.   static char canistr[] =
  937.   {24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
  938.    8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 0};
  939.  
  940.   zmputs(v, canistr);
  941. }                /* End of void canit() */
  942.  
  943. /**********************************************************
  944.  *      void zmputs(struct Vars *v, UBYTE *s)
  945.  *
  946.  * Send a string to the modem, with processing for \336 (sleep 1 sec)
  947.  * and \335 (break signal, ignored since XPR spec doesn't support it)
  948.  **********************************************************/
  949. void zmputs(struct Vars *v, UBYTE * s)
  950. {
  951.   UBYTE c;
  952.  
  953.   while (*s)
  954.   {
  955.     switch (c = *s++)
  956.     {
  957.     case '\336':
  958.       Delay(TICKS_PER_SECOND);
  959.     case '\335':
  960.       break;
  961.     default:
  962.       sendline(v, c);
  963.     }
  964.   }
  965.   sendbuf(v);
  966. }                /* End of void zmputs() */
  967.  
  968. /**********************************************************
  969.  *      void xsendline(struct Vars *v, UBYTE c)
  970.  *
  971.  * Write one character to the modem
  972.  **********************************************************/
  973. void xsendline(struct Vars *v, UBYTE c)
  974. {
  975.   v->Outbuf[v->Outbuflen++] = c;
  976.   if (v->Outbuflen >= sizeof(v->Outbuf))
  977.     sendbuf(v);
  978. }                /* End of void xsendline() */
  979.  
  980. /**********************************************************
  981.  *      void sendbuf(struct Vars *v)
  982.  *
  983.  * Send any data waiting in modem output buffer
  984.  **********************************************************/
  985. void sendbuf(struct Vars *v)
  986. {
  987.   if (v->Outbuflen)
  988.   {
  989.     /* Do the bookkeeping. */
  990.     v->BytesSent += v->Outbuflen;
  991.     (*v->io.xpr_swrite) (v->Outbuf, (long) v->Outbuflen);
  992.     v->Outbuflen = 0;
  993.   }
  994. }                /* End of void sendbuf() */
  995.  
  996. /**********************************************************
  997.  *      short readock(struct Vars *v, short tenths)
  998.  *
  999.  * Get a byte from the modem;
  1000.  * return TIMEOUT if no read within timeout tenths of a
  1001.  * second, return RCDO if carrier lost or other fatal error
  1002.  * (sread returns -1).  Added in some buffering so we
  1003.  * wouldn't hammer the system with single-byte serial port
  1004.  * reads.  Also, the buffering makes char_avail() a lot
  1005.  * easier to implement.
  1006.  **********************************************************/
  1007. short readock(struct Vars *v, short tenths)
  1008. {
  1009.   long t;
  1010.  
  1011.   /* If there's data waiting in our buffer, return next byte */
  1012.   if (v->Modemcount)
  1013.   {
  1014.   gotdata:
  1015.     --v->Modemcount;
  1016.     return (short) (*v->Modemchar++);
  1017.   }
  1018.   /* Our buffer is empty; see if there's anything waiting in system buffer.
  1019.    * If the caller is in a hurry, don't wait around, but if it can spare
  1020.    * a half second, wait a bit and build up some input so we don't do as
  1021.    * many sread() calls.
  1022.    */
  1023.   t = (tenths < 5) ? 0 : 500000;
  1024.   v->Modemcount = (*v->io.xpr_sread) (v->Modembuf, (long) sizeof(v->Modembuf),
  1025.                       t);
  1026.   if (v->Modemcount < 0)    /* Carrier dropped or other fatal error; abort */
  1027.   {
  1028.     v->Modemcount = 0;
  1029.     return RCDO;
  1030.   }
  1031.   else if (!v->Modemcount)    /* Nothing in system buffer; try waiting */
  1032.   {
  1033.     t = tenths * 100000L - t;
  1034.     v->Modemcount = (*v->io.xpr_sread) (v->Modembuf, 1L, t);
  1035.     if (v->Modemcount < 0)
  1036.     {
  1037.       v->Modemcount = 0;
  1038.       return RCDO;
  1039.     }
  1040.     else if (!v->Modemcount)    /* Nothing received in time */
  1041.       return TIMEOUT;
  1042.   }
  1043.  
  1044.   /* Do the bookkeeping. */
  1045.   v->BytesReceived += v->Modemcount;
  1046.  
  1047.   /* Reset buffer pointer to start of data */
  1048.   v->Modemchar = v->Modembuf;
  1049.   goto gotdata;
  1050. }                /* End of short readock() */
  1051.  
  1052. /**********************************************************
  1053.  *      char char_avail(struct Vars *v)
  1054.  *
  1055.  * Check if there's anything available to read from the
  1056.  * modem
  1057.  **********************************************************/
  1058. char char_avail(struct Vars *v)
  1059. {
  1060.   if (v->Modemcount)
  1061.     return TRUE;
  1062.  
  1063.   /* No data in our buffer; check system's input buffer */
  1064.   v->Modemcount = (*v->io.xpr_sread)
  1065.     (v->Modembuf, (long) sizeof(v->Modembuf), 0L);
  1066.   if (v->Modemcount < 1)    /* Nothing in system buffer either */
  1067.   {
  1068.     v->Modemcount = 0;
  1069.     return FALSE;
  1070.   }
  1071.   else
  1072.   {
  1073.     /* Do the bookkeeping. */
  1074.     v->BytesReceived += v->Modemcount;
  1075.  
  1076.     /* System buffer had something waiting for us */
  1077.     v->Modemchar = v->Modembuf;
  1078.     return TRUE;
  1079.   }
  1080. }                /* End of char char_avail() */
  1081.  
  1082. /**********************************************************
  1083.  *      void update_rate(struct Vars *v)
  1084.  *
  1085.  * Update the elapsed time, expected total time, and
  1086.  * effective data transfer rate values for status display
  1087.  **********************************************************/
  1088. void update_rate(struct Vars *v)
  1089. {
  1090.   ULONG elapsed, expect, throughput, time, oldrate = v->xpru.xpru_datarate;
  1091.   struct timeval Now;
  1092.  
  1093.   /* What time is it? */
  1094.   GetSysTime(&Now);
  1095.  
  1096.   /* Now calculate the time elapsed since the protocol started
  1097.    * transferring the current file.
  1098.    */
  1099.   if (CmpTime(&Now, &v->Starttime) > 0)
  1100.     Now.tv_secs = Now.tv_micro = 0;
  1101.   else
  1102.     SubTime(&Now, &v->Starttime);
  1103.   elapsed = Now.tv_secs;
  1104.  
  1105.   /* Now take a look at the data that go through so far. */
  1106.   if (v->Receiving)
  1107.     throughput = v->BytesReceived;
  1108.   else
  1109.     throughput = v->BytesSent;
  1110.  
  1111.   /* Cut off the lowest 24 bits. */
  1112.   if (throughput > 0x00FFFFFF)
  1113.     throughput = 0xFFFFFF00;
  1114.   else
  1115.     throughput = throughput << 8;
  1116.  
  1117.   /* Now calculate the time. */
  1118.   if (elapsed > 0x00FFFFFF)
  1119.     time = 0xFFFFFF00 | (Now.tv_micro >> 12);
  1120.   else
  1121.     time = (elapsed << 8) | (Now.tv_micro >> 12);
  1122.  
  1123.   /* Finally, calculate the effective number of characters
  1124.    * transferred per second.
  1125.    */
  1126.   if (time)
  1127.   {
  1128.       if(v->xpru.xpru_datarate = throughput / time)
  1129.         v->xpru.xpru_updatemask |= XPRU_DATARATE;
  1130.       else
  1131.         v->xpru.xpru_updatemask &= ~XPRU_DATARATE;
  1132.   }
  1133.   else
  1134.   {
  1135.     v->xpru.xpru_datarate = 0;
  1136.     v->xpru.xpru_updatemask &= ~XPRU_DATARATE;
  1137.   }
  1138.  
  1139.   /* Any changes? */
  1140.   if (v->xpru.xpru_datarate == oldrate)
  1141.     v->xpru.xpru_updatemask &= ~(XPRU_DATARATE|XPRU_EXPECTTIME);
  1142.  
  1143.   /* Compute expected total transfer time based on data rate so far */
  1144.   if (v->xpru.xpru_filesize <= 0 || !v->xpru.xpru_datarate)
  1145.     v->xpru.xpru_updatemask &= ~XPRU_EXPECTTIME;
  1146.   else
  1147.   {
  1148.     if ((expect = (v->xpru.xpru_filesize - v->Strtpos) / v->xpru.xpru_datarate) < elapsed)
  1149.       expect = elapsed;
  1150.  
  1151.     v->xpru.xpru_updatemask |= XPRU_EXPECTTIME;
  1152.   }
  1153.  
  1154.   /* Any data to display? */
  1155.   if (v->xpru.xpru_updatemask & XPRU_EXPECTTIME)
  1156.   {
  1157.     v->xpru.xpru_expecttime = (char *)v->Msgbuf;
  1158.  
  1159.     mysprintf(v->xpru.xpru_expecttime, "%02ld:%02ld:%02ld", expect / (60 * 60), (expect / 60) % 60, expect % 60);
  1160.  
  1161.     v->xpru.xpru_elapsedtime = &v->xpru.xpru_expecttime[strlen(v->xpru.xpru_expecttime) + 1];
  1162.   }
  1163.   else
  1164.     v->xpru.xpru_elapsedtime = v->Msgbuf;
  1165.  
  1166.   mysprintf(v->xpru.xpru_elapsedtime, "%02ld:%02ld:%02ld", elapsed / (60 * 60), (elapsed / 60) % 60, elapsed % 60);
  1167. }                /* End of void update_rate() */
  1168.  
  1169. /**********************************************************
  1170.  *      long bfopen(struct Vars *v, UBYTE *mode)
  1171.  *
  1172.  * Buffered file I/O fopen() interface routine
  1173.  **********************************************************/
  1174. long bfopen(struct Vars *v, UBYTE * mode)
  1175. {
  1176.   /* Initialize file-handling variables */
  1177.   v->Filebufpos = v->Filebuflen = v->Filebufcnt = 0;
  1178.   v->Fileflush = FALSE;
  1179.   v->Filebufptr = v->Filebuf;
  1180.   /* Open the file */
  1181.   return (*v->io.xpr_fopen) (v->Filename, mode);
  1182. }                /* End of long bfopen() */
  1183.  
  1184. /**********************************************************
  1185.  *      void bfclose(struct Vars *v)
  1186.  *
  1187.  * Buffered file I/O fclose() interface routine
  1188.  **********************************************************/
  1189. void bfclose(struct Vars *v)
  1190. {
  1191.   if (v->File)
  1192.   {
  1193.     /* If bfwrite() left data in buffer, flush it out before closing */
  1194.     if (v->Fileflush)
  1195.       (*v->io.xpr_fwrite) (v->Filebuf, 1L, v->Filebufcnt, v->File);
  1196.  
  1197.     /* Close the file */
  1198.     (*v->io.xpr_fclose) (v->File);
  1199.  
  1200.     /* Let's see if we should muck with the file attributes. */
  1201.     if (v->SetAttributes && (v->FileAttributes & 2))
  1202.     {
  1203.       ULONG Bits = 0, Seconds;
  1204.  
  1205.       /* Convert UTC to local time. */
  1206.       if (v->Modtime > GMT_Offset)
  1207.     Seconds = v->Modtime - GMT_Offset;
  1208.       else
  1209.     Seconds = 0;
  1210.  
  1211.       /* Take care of the owner bits. */
  1212.       if (v->Bits & 0100)
  1213.     Bits |= FIBF_EXECUTE;
  1214.  
  1215.       if (v->Bits & 0200)
  1216.     Bits |= FIBF_WRITE | FIBF_DELETE;
  1217.  
  1218.       if (v->Bits & 0400)
  1219.     Bits |= FIBF_READ;
  1220.  
  1221.       /* Now for the group bits. */
  1222.       if (!(v->Bits & 0010))
  1223.     Bits |= FIBF_GRP_EXECUTE;
  1224.  
  1225.       if (!(v->Bits & 0020))
  1226.     Bits |= FIBF_GRP_WRITE | FIBF_GRP_DELETE;
  1227.  
  1228.       if (!(v->Bits & 0040))
  1229.     Bits |= FIBF_GRP_READ;
  1230.  
  1231.       /* Now for the other bits. */
  1232.       if (!(v->Bits & 0001))
  1233.     Bits |= FIBF_OTR_EXECUTE;
  1234.  
  1235.       if (!(v->Bits & 0002))
  1236.     Bits |= FIBF_OTR_WRITE | FIBF_OTR_DELETE;
  1237.  
  1238.       if (!(v->Bits & 0004))
  1239.     Bits |= FIBF_OTR_READ;
  1240.  
  1241.       /* Try to change the attributes. */
  1242.       SetProtection(v->Filename, Bits);
  1243.  
  1244.       /* Now for the modification date. */
  1245.       if (Seconds)
  1246.       {
  1247.     struct DateStamp __aligned Date;
  1248.  
  1249.     Date.ds_Days = Seconds / 86400;
  1250.     Date.ds_Minute = (Seconds % 86400) / 60;
  1251.     Date.ds_Tick = (Seconds % 60) * TICKS_PER_SECOND;
  1252.  
  1253.     SetFileDate(v->Filename, &Date);
  1254.       }
  1255.     }
  1256.  
  1257.     v->File = NULL;
  1258.   }
  1259. }                /* End of void bfclose() */
  1260.  
  1261. /**********************************************************
  1262.  *      void bfseek(struct Vars *v, long pos)
  1263.  *
  1264.  * Buffered file I/O fseek() interface routine
  1265.  **********************************************************/
  1266. void bfseek(struct Vars *v, long pos)
  1267. {
  1268.   long offset;
  1269.  
  1270.   /* If new file position is within currently buffered section,
  1271.      reset pointers */
  1272.   if (pos >= v->Filebufpos && pos < v->Filebufpos + v->Filebuflen)
  1273.   {
  1274.     offset = pos - v->Filebufpos;
  1275.     v->Filebufptr = v->Filebuf + offset;
  1276.     v->Filebufcnt = v->Filebuflen - offset;
  1277.     /* Otherwise, fseek() file & discard buffer contents to force new read */
  1278.   }
  1279.   else
  1280.   {
  1281.     (*v->io.xpr_fseek) (v->File, pos, 0L);
  1282.     v->Filebuflen = v->Filebufcnt = 0;
  1283.     v->Filebufpos = pos;
  1284.   }
  1285. }                /* End of void bfseek() */
  1286.  
  1287. /**********************************************************
  1288.  *      long bfread(struct Vars *v, UBYTE *buf, long length)
  1289.  *
  1290.  * Buffered file I/O fread() interface routine
  1291.  **********************************************************/
  1292. long bfread(struct Vars *v, UBYTE * buf, long length)
  1293. {
  1294.   long count, total = 0;
  1295.  
  1296.   /* Keep going until entire request completed */
  1297.   while (length > 0)
  1298.   {
  1299.     /* Copy as much of the request as possible from the buffer */
  1300.     count = (length <= v->Filebufcnt) ? length : v->Filebufcnt;
  1301.     CopyMem(v->Filebufptr, buf, count);
  1302.     buf += count;
  1303.     total += count;
  1304.     length -= count;
  1305.     v->Filebufptr += count;
  1306.     v->Filebufcnt -= count;
  1307.  
  1308.     /* If we've emptied the buffer, read next buffer's worth */
  1309.     if (!v->Filebufcnt)
  1310.     {
  1311.       v->Filebufpos += v->Filebuflen;
  1312.       v->Filebufptr = v->Filebuf;
  1313.       v->Filebufcnt = v->Filebuflen
  1314.     = (*v->io.xpr_fread) (v->Filebuf, 1L, v->Filebufmax, v->File);
  1315.       /* If we hit the EOF, return with however much we read so far */
  1316.       if (!v->Filebufcnt)
  1317.     break;
  1318.     }
  1319.   }
  1320.   return total;
  1321. }                /* End of long bfread() */
  1322.  
  1323. /**********************************************************
  1324.  *      long bfwrite(struct Vars *v, UBYTE *buf, long length)
  1325.  *
  1326.  * Buffered file I/O fwrite() interface routine
  1327.  **********************************************************/
  1328. long bfwrite(struct Vars *v, UBYTE * buf, long length)
  1329. {
  1330.   long count, total = 0;
  1331.  
  1332.   /* Keep going until entire request completed */
  1333.   while (length > 0)
  1334.   {
  1335.     /* Copy as much as will fit into the buffer */
  1336.     count = v->Filebufmax - v->Filebufcnt;
  1337.     if (length < count)
  1338.       count = length;
  1339.     CopyMem(buf, v->Filebufptr, count);
  1340.     buf += count;
  1341.     total += count;
  1342.     length -= count;
  1343.     v->Filebufptr += count;
  1344.     v->Filebufcnt += count;
  1345.     v->Fileflush = TRUE;
  1346.  
  1347.     /* If we've filled the buffer, write it out */
  1348.     if (v->Filebufcnt == v->Filebufmax)
  1349.     {
  1350.       count = (*v->io.xpr_fwrite) (v->Filebuf, 1L, v->Filebufcnt, v->File);
  1351.       if (count < v->Filebufcnt)
  1352.     return -1;
  1353.       v->Filebufptr = v->Filebuf;
  1354.       v->Filebufcnt = 0;
  1355.       v->Fileflush = FALSE;
  1356.     }
  1357.   }
  1358.   return total;
  1359. }                /* End of long bfwrite() */
  1360.  
  1361. /**********************************************************
  1362.  *      void ioerr(struct XPR_IO *io, char *msg)
  1363.  *
  1364.  * Have the comm program display an error message for us,
  1365.  * using a temporary XPR_UPDATE structure; used to display
  1366.  * errors before Vars gets allocated
  1367.  **********************************************************/
  1368. void ioerr(struct XPR_IO *io, char *msg)
  1369. {
  1370.   struct XPR_UPDATE xpru;
  1371.  
  1372.   if (io->xpr_update)
  1373.   {
  1374.     xpru.xpru_updatemask = XPRU_ERRORMSG;
  1375.     xpru.xpru_errormsg = msg;
  1376.     (*io->xpr_update) (&xpru);
  1377.   }
  1378. }                /* End of void ioerr() */
  1379.  
  1380. /**********************************************************
  1381.  *      void upderr(struct Vars *v, char *msg)
  1382.  *
  1383.  * Have the comm program display an error message for us, using the
  1384.  * normal XPR_IO structure allocated in Vars
  1385.  **********************************************************/
  1386. void upderr(struct Vars *v, char *msg)
  1387. {
  1388.   v->xpru.xpru_updatemask = XPRU_ERRORMSG;
  1389.   v->xpru.xpru_errormsg = (STRPTR) msg;
  1390.   if ((STRPTR) msg == v->Msgbuf)    /* Ensure message length < 50 */
  1391.     msg[48] = '\0';
  1392.   (*v->io.xpr_update) (&v->xpru);
  1393. }                /* End of void upderr() */
  1394.  
  1395. /**********************************************************
  1396.  *      void updmsg(struct Vars *v,char *msg)
  1397.  *
  1398.  * Have the comm program display a normal message for us
  1399.  **********************************************************/
  1400. void updmsg(struct Vars *v, char *msg)
  1401. {
  1402.   v->xpru.xpru_updatemask = XPRU_MSG;
  1403.   v->xpru.xpru_msg = (STRPTR) msg;
  1404.   if ((STRPTR) msg == v->Msgbuf)    /* Ensure message length < 50 */
  1405.     msg[48] = '\0';
  1406.   (*v->io.xpr_update) (&v->xpru);
  1407. }                /* End of void updmsg() */
  1408.  
  1409. /**********************************************************
  1410.  *      long getfree(void)
  1411.  *
  1412.  * Figure out how many bytes are free on the drive we're uploading to.
  1413.  * Stubbed out for now; not supported by XPR spec.
  1414.  **********************************************************/
  1415. long getfree(struct Vars *v)
  1416. {
  1417.   LONG Space = 0x7FFFFFFF;    /* = Unknown */
  1418.  
  1419. /*
  1420.    UBYTE NameBuffer[256],*Index;
  1421.    BPTR FileLock;
  1422.    strcpy(NameBuffer,v -> Filename);
  1423.  
  1424.    Index = PathPart(NameBuffer);
  1425.  
  1426.    *Index = 0;
  1427.  
  1428.    if(FileLock = Lock(NameBuffer,ACCESS_READ))
  1429.    {
  1430.      struct InfoData __aligned InfoData;
  1431.  
  1432.      if(Info(FileLock,&InfoData))
  1433.      {
  1434.        if(InfoData . id_NumBlocks >= InfoData . id_NumBlocksUsed)
  1435.          Space = (InfoData . id_NumBlocks - InfoData . id_NumBlocksUsed) * InfoData . id_BytesPerBlock;
  1436.      }
  1437.  
  1438.      UnLock(FileLock);
  1439.    }
  1440. */
  1441.   return (Space);
  1442.  
  1443. }                /* End of long getfree() */
  1444.  
  1445. /**********************************************************
  1446.  *      char exist(struct Vars *v)
  1447.  *
  1448.  * Check whether file already exists; used to detect
  1449.  * potential overwrites
  1450.  **********************************************************/
  1451. char exist(struct Vars *v)
  1452. {
  1453.   long file;
  1454.  
  1455.   file = (*v->io.xpr_fopen) (v->Filename, "r");
  1456.   if (file)
  1457.   {
  1458.     (*v->io.xpr_fclose) (file);
  1459.     return TRUE;
  1460.   }
  1461.   else
  1462.     return FALSE;
  1463. }                /* End of char exist() */
  1464.  
  1465. /* End of Utils.c source */
  1466.